/**
 * \file glsl.h
 * GLSL Shader Support
 *
 * Provides:
 *	- class for loading shaders
 *	- program class, which combines a vertex & pixel shader
 *	  and provides methods for activation and passing parameters
 *
 * Todo list:
 *	- TODO: Type variable into GLSL Shader or create two classes for each shader
 **
 * Author: Mac Duy Hai.
 **
 * License... etc.
 */

#ifndef __GLSL_H__
#define __GLSL_H__

#include "graphics.h"
#include "glext.h"
#include "data.h"
#include "wtftypes.h"
#include "render.h"

// forward declaration
namespace WtfGraphics {
	class Mesh;
};

namespace WtfEngine {
	/**
	 * Base GLSL Shader (both Vertex and Fragment).
	 * On loading of the source the shader gets compiled and
	 * the handle to the compiled shader is stored.
	 */
	class GLSLShader : public DataObject  {
		private:
			GLhandleARB			hndShader;

		protected:
			/// Returns the GL handle to the shader
			GLhandleARB	getHandle() {
				return hndShader;
			};

			friend class GLSLProgram;

		public:
			// De/constructors
			GLSLShader();
			~GLSLShader();

			/// Loads and compiles the shader
			void Load(String sFilePath);

			DATA_OBJECT(GLSLShader, sizeof(GLSLShader));
	};

	/**
	 * Base GLSL Shader Program.
	 *
	 * Shaders are inserted into a Shader Program Object in order to be used.
	 * Currently, only allows 1 vertex shader and 1 pixel shader.
	 *
	 * Complete Shader Programs need to derive from this Base Program class
	 * and implement the Pass* functions, which should ship important attributes
	 * and variables (uniforms) to the shader.Also, they should implement the
	 * GetLocations(), which should get location handles to all variables in the shader.
	 */
	class GLSLProgram : public DataObject, public virtual IShader {
		private:
			GLSLShader::tRef		VertexShader;
			GLSLShader::tRef		FragmentShader;

		protected:
			/// Handle to the program
			GLhandleARB				hndProgram;

		public:
			/// Receives a Mesh object and passes chosen information from it to the shader.
			virtual void PassMesh(WtfGraphics::Mesh* mesh) {};

			/// Passes other globally accessible variables
			virtual void PassOther() {};

			/// Extracts all location handles
			virtual void GetLocations() {};

		public:
			//De/constructors
			GLSLProgram();
			virtual ~GLSLProgram();

			/// Inserts a Vertex shader and a pixel shader
			void Load(GLSLShader::tRef glslVertShader, GLSLShader::tRef glslFragShader);

			virtual void Activate() {
				glUseProgramObjectARB(hndProgram);
				checkGLError("activating GLSL program");
				PassOther();
			};

			/// Activates the program
			void Activate(WtfGraphics::Mesh* mesh) {
				glUseProgramObjectARB(hndProgram);
				PassMesh(mesh);
				PassOther();
			};

			/// Deactivates the program
			virtual void Deactivate() {
				glUseProgramObjectARB(0);
			};

			int GetUniformLocation(const char * s) {
				return glGetUniformLocationARB(hndProgram, s);
			};

			// Reference
			typedef WtfEngine::GcReference<GLSLProgram>			tRef;
	};

	/**
	 * \brief Space for complete GLSL shader program classes.
	 * Shaders Programs implemented in this section are shader program classes.
	 * They are free to load any shader implementations, but these shaders sources
	 * must conform specific 'protocols' in order to be valid.
	 * Shader Programs in this space are simply called "shaders" for simplicity.
	 */
	namespace ShaderPrograms {
		/**
		 * Specular Shader Class.
		 * This shader is fairly stand-alone, it only requires a camera position.
		 */
		class SpecularShader : public GLSLProgram {
			private:
				// location handles
				int		hndCamPosition;
				int		hndTexture;

			protected:
				// get location handles
				void GetLocations() {
					hndCamPosition = glGetUniformLocationARB(hndProgram, "cameraPosition");
					hndTexture 	   = glGetUniformLocationARB(hndProgram, "tex");
				};

				// overloaded pass functions
				void PassMesh(WtfGraphics::Mesh* mesh);
				void PassOther();

			public:
				SpecularShader() {};
				~SpecularShader() {};
		};

		/**
		 * Parallax Normal Shader.
		 * This shader class provides normal mapping combined with parallax mapping. This
		 * creates a very advanced bump-mapping effect with an apparent depth realism.
		 */
		class NormalShader : public SpecularShader {
			private:
				// location handles
				int	hndNormalTexture;
				int hndHeightTexture;
				int hndTangent;

			protected:
				void GetLocations() {
					SpecularShader::GetLocations();
					hndNormalTexture = glGetUniformLocationARB(hndProgram, "ntex");
					hndHeightTexture = glGetUniformLocationARB(hndProgram, "htex");
					hndTangent 		 = glGetAttribLocationARB(hndProgram, "tangent");
				}

				// overloaded methods
				void PassMesh(WtfGraphics::Mesh* mesh);
				void Deactivate();
			public:
				NormalShader() {};
				~NormalShader() {};
		};
	};
};

#endif
